﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Syspetro.Core.EventBus
{
    public class EventBus
    {
        private readonly EventHandlerContainer eventHandler = new();
        private readonly object Lock = new();
        private bool IsTrigger { get; set; } = false;
        /// <summary>
        /// 定义线程安全集合
        /// </summary>
        private readonly ConcurrentDictionary<string, List<IEventBase>> _eventAndHandlerMapping = new();
        public void Subscribe<T, THandler>(string group = null) where T : IEventBase where THandler : IEventHandler<T>
        {
            eventHandler.Subscribe(typeof(T), typeof(THandler), group);
        }
        public void Subscribe(Type type, Type typeHandler, string group = null)
        {
            eventHandler.Subscribe(type, typeHandler, group);
        }
        private void SetTrigger(bool bl)
        {
            lock (Lock)
            {
                IsTrigger = bl;
            }
        }
        /// <summary>
        /// 消息添加队列并尝试发布
        /// </summary>
        /// <typeparam name="TEventData"></typeparam>
        /// <param name="eventHandler"></param>
        /// <param name="group"></param>
        public void RegisterAsync<TEventData>(TEventData eventHandler, string group = null) where TEventData : IEventBase
        {
            var task = new Task(() =>
            {
                Register(eventHandler, group);
            });
            task.Start();
            return;
        }
        /// <summary>
        /// 消息添加队列并尝试发布
        /// </summary>
        /// <typeparam name="TEventData"></typeparam>
        /// <param name="eventData"></param>
        /// <param name="group"></param>
        public void Register<TEventData>(TEventData eventData, string group = null) where TEventData : IEventBase
        {
            if (!IsTrigger)
            {
                Trigger(eventData, group);
                NextTrigger();
            }
            else
            {
                var type = EventHandlerContainer.GetEventKey(typeof(TEventData), group);
                if (!_eventAndHandlerMapping.TryGetValue(type, out List<IEventBase> handlerTypes) || handlerTypes == null)
                {
                    handlerTypes = new List<IEventBase> { eventData };
                }
                else
                {
                    handlerTypes.Add(eventData);
                }
                _eventAndHandlerMapping[type] = handlerTypes;
                if (!IsTrigger) Trigger();
            }
        }
        /// <summary>
        /// 触发绑定的事件
        /// </summary>
        public void Trigger()
        {
            if (!_eventAndHandlerMapping.IsEmpty)
            {
                Trigger(_eventAndHandlerMapping.Keys.First());
            }
        }
        /// <summary>
        /// 根据事件源类型触发绑定的事件处理
        /// </summary>
        /// <param name="key"></param>
        private void Trigger(string key)
        {
            try
            {
                if (_eventAndHandlerMapping.TryRemove(key, out List<IEventBase> events))
                {
                    if (!IsTrigger)
                    {
                        SetTrigger(true);
                    }
                    foreach (var e in events)
                    {
                        var group = EventHandlerContainer.GetEventGroup(key);
                        Trigger(e, group);
                    }
                }
            }
            finally
            {
                NextTrigger();
            }
        }
        /// <summary>
        /// 根据事件源触发绑定的事件处理
        /// </summary>
        /// <typeparam name="TEventData"></typeparam>
        /// <param name="eventData"></param>
        /// <param name="group"></param>
        private void Trigger<TEventData>(TEventData eventData, string group) where TEventData : IEventBase
        {
            int retry = eventData.Retry;
            if (!IsTrigger)
            {
                SetTrigger(true);
            }
            if (!eventHandler.Publish(eventData, group))
            {
                while (retry > 0)//重试
                {
                    if (eventHandler.Publish(eventData, group)) break;
                    retry--;
                }
            }
        }
        private void NextTrigger()
        {
            if (!_eventAndHandlerMapping.IsEmpty)
            {
                Trigger(_eventAndHandlerMapping.Keys.First());
            }
            else
            {
                SetTrigger(false);
            }
        }
    }
}
